import { SCondition, SFieldOperator, SFields } from '@dataui/crud-request';
import { objKeys, is_object } from '@dataui/crud-util';
import {
  FindManyOptionsReq,
  FindOperatorReq,
  FindOptionsWhereReq,
  isFindOperatorReq,
} from '@nest-crud/request-typeorm/find-options';
interface _entity {
  field: string | number | boolean | Date | object;
}
export function deepSetConditions(findOptions: FindManyOptionsReq): SCondition | undefined {
  let search: SCondition;
  if (findOptions.where) {
    const deepSetCondition = (condition: SFields, where: FindOptionsWhereReq<_entity>, parents: string[] = []) => {
      objKeys(where).forEach((field) => {
        const fieldChain = parents.concat(field);
        const item = where[field as keyof _entity];
        if (!is_object(item)) {
          condition[fieldChain.join('.')] = item;
        } else {
          if (isFindOperatorReq(item)) {
            const sFieldOperator = getSFieldOperatorType(item);
            if (sFieldOperator) {
              let value = item.v;
              if (
                (
                  [
                    '$ends',
                    '$endsL',
                    '$starts',
                    '$startsL',
                    '$cont',
                    '$contL',
                    '$excl',
                    '$exclL',
                  ] as (keyof SFieldOperator)[]
                ).includes(sFieldOperator)
              ) {
                value = (value as string).replace(/^%|%$/, '');
              }
              condition[fieldChain.join('.')] = { [sFieldOperator]: value };
            }
          } else {
            if (item instanceof Date) {
              item;
            } else if (Array.isArray(item)) {
              item;
            } else {
              deepSetCondition(condition, item, fieldChain);
            }
          }
        }
      });
    };
    const wheres = Array.isArray(findOptions.where) ? findOptions.where : [findOptions.where];
    const conditions: SFields[] = [];

    wheres.forEach((where) => {
      const condition: SFields = {};
      deepSetCondition(condition, where);
      conditions.push(condition);
    });
    if (conditions.length === 1) {
      search = conditions[0];
    } else {
      search = { $or: conditions };
    }
  }
  return search;
}
function getSFieldOperatorType(findOperator: FindOperatorReq<any>): keyof SFieldOperator | undefined {
  let sFieldOperator: keyof SFieldOperator;

  if (findOperator.t === 'not') {
    const value = findOperator.v;
    const realValue = value.v as string;

    if (isFindOperatorReq(value)) {
      switch (value.t) {
        case 'any':
          sFieldOperator = undefined;
          break;
        case 'arrayContainedBy':
          sFieldOperator = undefined;
          break;
        case 'arrayContains':
          sFieldOperator = undefined;
          break;
        case 'arrayOverlap':
          sFieldOperator = undefined;
          break;
        case 'between':
          sFieldOperator = undefined;
          break;
        case 'equal':
          sFieldOperator = '$ne';
          break;
        case 'ilike':
          if (realValue.startsWith('%') && realValue.endsWith('%')) {
            sFieldOperator = '$exclL';
          } else {
            // [ ]
          }
          break;
        case 'in':
          sFieldOperator = '$notin';
          break;
        case 'isNull':
          sFieldOperator = '$notnull';
          break;
        case 'lessThan':
          sFieldOperator = '$gte';
          break;
        case 'lessThanOrEqual':
          sFieldOperator = '$gt';
          break;
        case 'like':
          if (realValue.startsWith('%') && realValue.endsWith('%')) {
            sFieldOperator = '$excl';
          } else {
            // [ ]
          }
          break;
        case 'moreThan':
          sFieldOperator = '$lte';
          break;
        case 'moreThanOrEqual':
          sFieldOperator = '$lt';
          break;
        case 'raw':
          break;

        default:
          break;
      }
    } else {
      sFieldOperator = '$ne';
    }
  } else {
    const realValue = findOperator.v as string;

    switch (findOperator.t) {
      case 'any':
        break;
      case 'arrayContainedBy':
        break;
      case 'arrayContains':
        break;
      case 'arrayOverlap':
        break;
      case 'between':
        sFieldOperator = '$between';
        break;
      case 'equal':
        sFieldOperator = '$eq';
        break;
      case 'ilike':
        if (realValue.startsWith('%') && realValue.endsWith('%')) {
          sFieldOperator = '$contL';
        } else if (realValue.startsWith('%')) {
          sFieldOperator = '$endsL';
        } else if (realValue.endsWith('%')) {
          sFieldOperator = '$startsL';
        } else {
          sFieldOperator = '$contL';
        }
        break;
      case 'in':
        sFieldOperator = '$in';
        break;
      case 'isNull':
        sFieldOperator = '$isnull';
        break;
      case 'lessThan':
        sFieldOperator = '$lt';
        break;
      case 'lessThanOrEqual':
        sFieldOperator = '$lte';
        break;
      case 'like':
        if (realValue.startsWith('%') && realValue.endsWith('%')) {
          sFieldOperator = '$cont';
        } else if (realValue.startsWith('%')) {
          sFieldOperator = '$ends';
        } else if (realValue.endsWith('%')) {
          sFieldOperator = '$starts';
        } else {
          sFieldOperator = '$cont';
        }
        break;
      case 'moreThan':
        sFieldOperator = '$gt';
        break;
      case 'moreThanOrEqual':
        sFieldOperator = '$gte';
        break;
      case 'raw':
        sFieldOperator;
        break;

      default:
        break;
    }
  }
  return sFieldOperator;
}
